import Cocoa
import Foundation

// 任务栏控制器 - 管理多显示器任务栏
class TaskbarController: NSObject {
    private var taskbarWindows: [TaskbarWindow] = []
    var windowResizeMonitor: WindowResizeMonitor?
    private weak var windowManager: WindowManager?
    
    override init() {
        super.init()
        setupScreenChangeNotifications()
        
        // 监听窗口更新通知
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(windowsDidUpdate(_:)),
            name: .windowsDidUpdate,
            object: nil
        )
        
        // 事件驱动刷新：监听窗口变化
        self.windowResizeMonitor = WindowResizeMonitor { [weak self] in
            self?.windowManager?.refreshWindows()
        }

        // 事件驱动：监听窗口变化，触发刷新
        self.windowResizeMonitor = WindowResizeMonitor { [weak self] in
            self?.windowManager?.refreshWindows()
        }
    }
    
    func setWindowManager(_ manager: WindowManager) {
        self.windowManager = manager
    }
    
    func setupTaskbars() {
        // 清除现有任务栏
        taskbarWindows.forEach { $0.close() }
        taskbarWindows.removeAll()
        
        // 为每个屏幕创建任务栏
        for (index, screen) in NSScreen.screens.enumerated() {
            let taskbarWindow = TaskbarWindow(screen: screen, screenIndex: index)
            taskbarWindows.append(taskbarWindow)
            taskbarWindow.makeKeyAndOrderFront(self)
        }
        
        // 调整现有窗口以避开任务栏
        if let manager = windowManager {
            adjustWindowsForTaskbar(windows: manager.windows)
        }
    }
    
    private func setupScreenChangeNotifications() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(screenConfigurationChanged),
            name: NSApplication.didChangeScreenParametersNotification,
            object: nil
        )
    }
    
    @objc private func screenConfigurationChanged() {
        setupTaskbars()
    }
    
    @objc private func windowsDidUpdate(_ notification: Notification) {
        // print("🔔 接收到窗口更新通知")
        if let windows = notification.object as? [WindowInfo] {
            // print("🔔 通知包含 \(windows.count) 个窗口，开始调整窗口")
            adjustWindowsForTaskbar(windows: windows)
        } else {
            // print("❌ 通知对象不是WindowInfo数组")
        }
    }
    
    func adjustWindowsForTaskbar(windows: [WindowInfo]) {
        // print("🔧 开始调整 \(windows.count) 个窗口以避开任务栏")
        for window in windows {
            // print("🔍 处理窗口: \(window.appName) - \(window.windowTitle) (屏幕\(window.screenNumber))")
            adjustWindowForTaskbar(
                windowId: window.windowId,
                processId: window.processId,
                currentBounds: window.bounds,
                screenNumber: window.screenNumber
            )
        }
    }
    
    private func adjustWindowForTaskbar(windowId: CGWindowID, processId: pid_t, currentBounds: CGRect, screenNumber: Int) {
        // 获取应用的 AX element
        let app = AXUIElementCreateApplication(processId)
        
        // 获取应用的所有窗口
        var windowsRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(app, kAXWindowsAttribute as CFString, &windowsRef) != .success {
            return
        }
        
        guard let windows = windowsRef as? [AXUIElement] else { return }
        
        // 屏幕信息（日志已停用）
        let screens = NSScreen.screens
        guard screenNumber < screens.count else { 
            // print("❌ 屏幕编号 \(screenNumber) 超出范围")
            return 
        }
        let screen = screens[screenNumber]
        let screenFrame = screen.frame
        let visibleFrame = screen.visibleFrame
        let taskbarHeight: CGFloat = 44
        // 目标最大化高度 = visibleFrame 高度再减去任务栏高度
        let targetHeight = visibleFrame.height - taskbarHeight
        // print("📺 屏幕 \(screenNumber) frame=\(screenFrame) visible=\(visibleFrame) targetHeight(with taskbar)=\(targetHeight)")
        
        // 对每个窗口检查是否需要调整
        for axWindow in windows {
            // 锁定到当前 WindowInfo 对应的 AX 窗口（标题优先，其次坐标近似）
            let ok = matchesWindow(axWindow, to: currentBounds, title: windowManager?.windows.first { $0.bounds == currentBounds }?.windowTitle, on: screen)
            if !ok {
                // 宽松策略：若窗口接近该屏全宽，仍尝试按重叠规则进行避让
                // 这样可以覆盖某些 Electron/NW.js 应用标题变更或不可读的情况
                // 不直接 continue
            }

            // 读取该 AX 窗口的实际矩形（BL 坐标）
            guard let axRect = getAXRect(axWindow, on: screen) else { continue }

            let currentTop = axRect.maxY
            let bottomLimit = max(visibleFrame.minY, screenFrame.minY + taskbarHeight)
            let widthTol: CGFloat = 20
            let nearFullWidth = abs(axRect.width - screenFrame.width) < widthTol

            let isMax = isWindowMaximized(axWindow, currentBounds: axRect, screen: screen)
            let overlapsTaskbar = axRect.minY < bottomLimit
            let exceedsTargetHeight = axRect.height > targetHeight
            let shouldAdjust = isMax || (nearFullWidth && (overlapsTaskbar || exceedsTargetHeight))
            guard shouldAdjust else { continue }

            var newFrame = axRect
            let newHeight = max(0, min(currentTop - bottomLimit, targetHeight))
            newFrame.size.height = newHeight
            newFrame.origin.y = currentTop - newHeight
            // print("🔧 调整窗口，从 \(axRect) 到 \(newFrame)")
            adjustMaximizedWindow(axWindow, to: newFrame, on: screen)
        }
    }

    private func isWindowMaximized(_ window: AXUIElement, currentBounds: CGRect, screen: NSScreen) -> Bool {
        // 检查是否为全屏窗口
        var fullRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(window, "AXFullScreen" as CFString, &fullRef) == .success,
           let full = fullRef as? Bool, full {
            return true
        }

        // 通过窗口大小判断是否接近最大化
        var positionRef: CFTypeRef?
        var sizeRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(window, kAXPositionAttribute as CFString, &positionRef) == .success,
           AXUIElementCopyAttributeValue(window, kAXSizeAttribute as CFString, &sizeRef) == .success,
           let positionValue = positionRef,
           let sizeValue = sizeRef {
            var position = CGPoint.zero
            var size = CGSize.zero
            if AXValueGetValue(positionValue as! AXValue, .cgPoint, &position) &&
               AXValueGetValue(sizeValue as! AXValue, .cgSize, &size) {
                let tolerance: CGFloat = 20
                let isMaxWidth = abs(size.width - screen.frame.width) < tolerance
                let vf = screen.visibleFrame
                let isMaxHeight = abs(size.height - screen.frame.height) < tolerance ||
                                   abs(size.height - vf.height) < tolerance ||
                                   abs(size.height - (vf.height - 44)) < tolerance
                // print("🔍 检查窗口最大化: 窗口(\(size.width)x\(size.height)) vs 屏幕(\(screen.frame.width)x\(screen.frame.height))")
                // print("📏 宽度匹配: \(isMaxWidth), 高度匹配: \(isMaxHeight), 最大化: \(isMaxWidth && isMaxHeight)")
                return isMaxWidth && isMaxHeight
            }
        }
        return false
    }
    
    private func isWindowAlreadyAdjusted(_ window: AXUIElement, screen: NSScreen, taskbarHeight: CGFloat) -> Bool {
        var positionRef: CFTypeRef?
        var sizeRef: CFTypeRef?
        
        if AXUIElementCopyAttributeValue(window, kAXPositionAttribute as CFString, &positionRef) == .success,
           AXUIElementCopyAttributeValue(window, kAXSizeAttribute as CFString, &sizeRef) == .success,
           let positionValue = positionRef,
           let sizeValue = sizeRef {
            
            var position = CGPoint.zero
            var size = CGSize.zero
            
            if AXValueGetValue(positionValue as! AXValue, .cgPoint, &position) &&
               AXValueGetValue(sizeValue as! AXValue, .cgSize, &size) {
                
                let screenFrame = screen.frame
                let expectedAdjustedHeight = screenFrame.height - taskbarHeight
                
                // 检查窗口高度是否已经是调整后的高度（允许小误差）
                let heightTolerance: CGFloat = 10
                let isAlreadyAdjusted = abs(size.height - expectedAdjustedHeight) < heightTolerance
                
                // print("🔍 检查窗口是否已调整: 当前高度 \(size.height), 预期调整后高度 \(expectedAdjustedHeight), 已调整: \(isAlreadyAdjusted)")
                
                return isAlreadyAdjusted
            }
        }
        
        return false
    }
    
    private func isWindowAlreadyAdjustedActual(_ window: AXUIElement, screen: NSScreen, taskbarHeight: CGFloat) -> Bool {
        var sizeRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(window, kAXSizeAttribute as CFString, &sizeRef) == .success,
           let sizeValue = sizeRef {
            var size = CGSize.zero
            if AXValueGetValue(sizeValue as! AXValue, .cgSize, &size) {
                let expectedHeight = screen.frame.height - taskbarHeight
                let tolerance: CGFloat = 10
                return abs(size.height - expectedHeight) < tolerance
            }
        }
        return false
    }
    
    private func adjustMaximizedWindow(_ window: AXUIElement, to newFrame: CGRect, on screen: NSScreen) {
        // print("🔧 调整最大化窗口到新边界: \(newFrame)")
        
        // 如果窗口处于全屏状态，先退出全屏
        var fullRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(window, "AXFullScreen" as CFString, &fullRef) == .success,
           let full = fullRef as? Bool, full {
            // print("📺 检测到全屏窗口，退出全屏状态")
            AXUIElementSetAttributeValue(window, "AXFullScreen" as CFString, false as CFBoolean)
            // 等待短时间，让系统退出全屏
            Thread.sleep(forTimeInterval: 0.08)
        }
        
    // 设置新的窗口位置和大小（AX 需要 top-left 坐标系）
    let topY = screen.frame.maxY
    let axTopLeftY = topY - (newFrame.origin.y + newFrame.size.height)
    var position = CGPoint(x: newFrame.minX, y: axTopLeftY)
        var size = CGSize(width: newFrame.width, height: newFrame.height)
        // print("📐 设置窗口位置: \(position), 大小: \(size)")
        
        // 设置位置
        let positionValue = AXValueCreate(AXValueType.cgPoint, &position)
        _ = AXUIElementSetAttributeValue(window, kAXPositionAttribute as CFString, positionValue!)
        // print("📍 设置位置结果: \(positionResult == .success ? "成功" : "失败")")
        
        // 设置大小
        let sizeValue = AXValueCreate(AXValueType.cgSize, &size)
        _ = AXUIElementSetAttributeValue(window, kAXSizeAttribute as CFString, sizeValue!)
        // print("📏 设置大小结果: \(sizeResult == .success ? "成功" : "失败")")
    }

    private func matchesWindow(_ axWindow: AXUIElement, to rect: CGRect, title: String?, on screen: NSScreen) -> Bool {
        // 先尝试用标题匹配（最精准）
        if let title = title {
            var titleRef: CFTypeRef?
            if AXUIElementCopyAttributeValue(axWindow, kAXTitleAttribute as CFString, &titleRef) == .success,
               let axTitle = titleRef as? String, !axTitle.isEmpty {
                if axTitle == title { return true }
            }
        }

        // 回退到坐标近似匹配
        var posRef: CFTypeRef?
        var sizeRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(axWindow, kAXPositionAttribute as CFString, &posRef) == .success,
           AXUIElementCopyAttributeValue(axWindow, kAXSizeAttribute as CFString, &sizeRef) == .success,
           let pv = posRef, let sv = sizeRef {
            var p = CGPoint.zero
            var s = CGSize.zero
            if AXValueGetValue(pv as! AXValue, .cgPoint, &p) && AXValueGetValue(sv as! AXValue, .cgSize, &s) {
                // 将 AX 的 top-left 坐标转换为 bottom-left 坐标（与 WindowManager 同一基准），使用该屏幕的 topY
                let converted = rectTopLeftToBottomLeft(originTopLeft: p, size: s, topY: screen.frame.maxY)
                let dx = abs(converted.origin.x - rect.origin.x)
                let dy = abs(converted.origin.y - rect.origin.y)
                let dw = abs(s.width - rect.size.width)
                let dh = abs(s.height - rect.size.height)
                    let tol: CGFloat = 80
                let match = dx < tol && dy < tol && dw < tol && dh < tol
                if !match {
                    // print("⏭️ 跳过不匹配窗口，AX-TL=(\(p),\(s)) conv-BL=\(converted) vs currentBounds=\(rect)")
                }
                return match
            }
        }
        return false
    }

    private func getAXRect(_ axWindow: AXUIElement, on screen: NSScreen) -> CGRect? {
        var posRef: CFTypeRef?
        var sizeRef: CFTypeRef?
        guard AXUIElementCopyAttributeValue(axWindow, kAXPositionAttribute as CFString, &posRef) == .success,
              AXUIElementCopyAttributeValue(axWindow, kAXSizeAttribute as CFString, &sizeRef) == .success,
              let pv = posRef, let sv = sizeRef else { return nil }
        var p = CGPoint.zero
        var s = CGSize.zero
        guard AXValueGetValue(pv as! AXValue, .cgPoint, &p), AXValueGetValue(sv as! AXValue, .cgSize, &s) else { return nil }
        let rect = rectTopLeftToBottomLeft(originTopLeft: p, size: s, topY: screen.frame.maxY)
        return rect
    }
}

// 窗口大小调整监听器
class WindowResizeMonitor {
    private var observers: [AXObserver] = []
    private var changeCallback: (() -> Void)?
    private var retryTimer: Timer?
    
    init(changeCallback: (() -> Void)? = nil) {
        self.changeCallback = changeCallback
        // 若未授权，循环重试直到获得 AX 权限
        if AXIsProcessTrusted() {
            startMonitoring()
        } else {
            retryTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] t in
                if AXIsProcessTrusted() {
                    t.invalidate()
                    self?.retryTimer = nil
                    self?.startMonitoring()
                    DispatchQueue.main.async {
                        self?.changeCallback?()
                    }
                }
            }
        }
    }
    
    private func startMonitoring() {
        // 监听所有运行应用的窗口变化
        let runningApps = NSWorkspace.shared.runningApplications
        
        for app in runningApps {
            if app.activationPolicy == .regular {
                setupObserverForApp(app)
            }
        }
        
        // 监听新应用启动
        NSWorkspace.shared.notificationCenter.addObserver(
            forName: NSWorkspace.didLaunchApplicationNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            if let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication {
                self?.setupObserverForApp(app)
            }
        }
    }
    
    private func setupObserverForApp(_ app: NSRunningApplication) {
        guard app.activationPolicy == .regular else { return }
        
        let appElement = AXUIElementCreateApplication(app.processIdentifier)
        var observer: AXObserver?
        
    let result = AXObserverCreate(app.processIdentifier, { obs, element, notification, userData in
            // 在新窗口创建时，给该窗口附加详细通知
            if CFStringCompare(notification, kAXWindowCreatedNotification as CFString, []) == .compareEqualTo {
                let notis: [CFString] = [
                    kAXWindowMovedNotification as CFString,
                    kAXWindowResizedNotification as CFString,
                    kAXWindowMiniaturizedNotification as CFString,
                    kAXWindowDeminiaturizedNotification as CFString,
                    kAXUIElementDestroyedNotification as CFString,
                    kAXTitleChangedNotification as CFString
                ]
                for n in notis {
                    AXObserverAddNotification(obs, element, n, userData)
                }
            }

            // 统一触发刷新
            if let callback = Unmanaged<AnyObject>.fromOpaque(userData!).takeUnretainedValue() as? (() -> Void) {
                DispatchQueue.main.async {
                    callback()
                }
            }
    }, &observer)
        
        guard result == .success, let observer = observer else { return }
        
        // 创建回调闭包的包装
        let callbackWrapper = { [weak self] in
            self?.changeCallback?()
        }
        let userData = Unmanaged.passRetained(callbackWrapper as AnyObject).toOpaque()
        
        // 在 appElement 上监听“窗口创建/焦点变化”等事件
        let appLevelNotis: [CFString] = [
            kAXWindowCreatedNotification as CFString,
            kAXFocusedWindowChangedNotification as CFString,
            kAXMainWindowChangedNotification as CFString
        ]
        for n in appLevelNotis {
            AXObserverAddNotification(observer, appElement, n, userData)
        }

        // 为当前已有的窗口附加详细通知
        var winsRef: CFTypeRef?
        if AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &winsRef) == .success,
           let wins = winsRef as? [AXUIElement] {
            let notis: [CFString] = [
                kAXWindowMovedNotification as CFString,
                kAXWindowResizedNotification as CFString,
                kAXWindowMiniaturizedNotification as CFString,
                kAXWindowDeminiaturizedNotification as CFString,
                kAXUIElementDestroyedNotification as CFString,
                kAXTitleChangedNotification as CFString
            ]
            for w in wins {
                for n in notis {
                    AXObserverAddNotification(observer, w, n, userData)
                }
            }
        }
        
        // 将观察器添加到运行循环
        CFRunLoopAddSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .defaultMode)
        
        observers.append(observer)
    }
    
    deinit {
        for observer in observers {
            CFRunLoopRemoveSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .defaultMode)
        }
    }
}